#include <irrlicht.h>
#include <string>
#include <sstream>
#include <irrXML.h>
#include <vector>

#include "game.h"
#include "MyEventReceiver.h"

using namespace irr;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

struct RenderData {
	scene::IAnimatedMeshSceneNode* chassisNode;
	std::vector<scene::ISceneNode*> groundNodes;
	scene::ISceneNode* groundNode[2];
	scene::ISceneNode* wallNodes[10];

	RenderData() {
		chassisNode = NULL;
		for(int i = 0; i < 2; i++)
			groundNode[i] = NULL;
		for(int i = 0; i < 10; i++) {
			wallNodes[i] = NULL;
		}
		groundNodes.clear();
	}
} g_renderData;

struct BlockData {
	f32 xStart, yStart, xEnd, yEnd;
	core::stringc texture;

	BlockData() {
		xStart = 0;
		yStart = 0;
		xEnd = 0;
		yEnd = 0;
		texture = "";
	}
};

struct LandscapeData {
	f32 width, height, blocksfactor, blocksheight, x, y;
	std::vector<BlockData> blocks;

	LandscapeData() {
		width = 0;
		height = 0;
		blocksfactor = 0;
		blocksheight = 0;
		x = 0;
		y = 0;
		blocks.clear();
	}
} g_landscapeData;

static void readXML(const char* file) {
	io::IrrXMLReader* xml = io::createIrrXMLReader(file);

	while(xml && xml->read()) {
		switch(xml->getNodeType()) {
		case io::EXN_ELEMENT:
			{
				if(strcmp("landscape", xml->getNodeName()) == 0) {
					g_landscapeData.width = atof(xml->getAttributeValue("width"));
					g_landscapeData.height = atof(xml->getAttributeValue("height"));
					g_landscapeData.x = atof(xml->getAttributeValue("x"));
					g_landscapeData.y = atof(xml->getAttributeValue("y"));
					g_landscapeData.blocksfactor = atof(xml->getAttributeValue("blocksfactor"));
					g_landscapeData.blocksheight = atof(xml->getAttributeValue("blocksheight"));
				}
				if(strcmp("block", xml->getNodeName()) == 0) {
					BlockData tmp;
					tmp.xStart = atof(xml->getAttributeValue("xStart"));
					tmp.yStart = atof(xml->getAttributeValue("yStart"));
					tmp.xEnd = atof(xml->getAttributeValue("xEnd"));
					tmp.yEnd = atof(xml->getAttributeValue("yEnd"));
					tmp.texture = xml->getAttributeValue("texture");
					g_landscapeData.blocks.push_back(tmp);
				}
			}
			break;
		}
	}

	delete xml;
}

static void renderScene(Game* app, video::IVideoDriver* driver, scene::ISceneManager* smgr) {
	//render the wall
	{
		for(int i = 0; i < 10; i++) {
			if(g_renderData.wallNodes[i]) {
				hkVector4 tmp = app->m_wall[i]->getPosition();
				hkQuaternion quaternion = app->m_wall[i]->getRotation();
				g_renderData.wallNodes[i]->setPosition(core::vector3df(tmp(0), tmp(1), tmp(2)));
				if(quaternion.hasValidAxis()) {
					hkReal angle = quaternion.getAngle() * 180.0f / HK_REAL_PI;
					hkVector4 axis;
					quaternion.getAxis(axis);
					g_renderData.wallNodes[i]->setRotation(core::vector3df(axis(0)*angle, axis(1)*angle, axis(2)*angle));
				}
			}
		}
	}

	//render the vehicle
	{
		if(g_renderData.chassisNode) {
			hkVector4 tmp = app->m_vehicle->getChassis()->getPosition();
			hkQuaternion quaternion = app->m_vehicle->getChassis()->getRotation();
			g_renderData.chassisNode->setPosition(core::vector3df(tmp(0), tmp(1), tmp(2)));

			smgr->getActiveCamera()->setPosition(core::vector3df(app->out.m_positionWS(0), app->out.m_positionWS(1), app->out.m_positionWS(2)));
			smgr->getActiveCamera()->setTarget(core::vector3df(app->out.m_lookAtWS(0), app->out.m_lookAtWS(1), app->out.m_lookAtWS(2)));

			if(quaternion.hasValidAxis()) {
				hkReal angle = quaternion.getAngle() * 180.0f / HK_REAL_PI;
				hkVector4 axis;
				quaternion.getAxis(axis);
				g_renderData.chassisNode->setRotation(core::vector3df(axis(0)*angle, axis(1)*angle, axis(2)*angle));
			}
		}
	}
}

int main() {
	readXML("./media/mappa.xml");

	MyEventReceiver receiver;
	//IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(1366, 768), 32, true, false, true, &receiver);
	IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
	if(!device)
		return 1;

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();

	device->getCursorControl()->setVisible(false);

	scene::ICameraSceneNode* cam = smgr->addCameraSceneNode();
	smgr->getActiveCamera()->setNearValue(0.1f);

	scene::ISceneNode* lightNode = smgr->addLightSceneNode(0, core::vector3df(200, 800, 0), video::SColorf(1.0f, 1.0f, 1.0f, 1.0f), 800.0f);

	scene::IAnimatedMesh* mesh = smgr->getMesh("./media/micra/micra.3ds");
	g_renderData.chassisNode = smgr->addAnimatedMeshSceneNode(mesh);
	g_renderData.chassisNode->addShadowVolumeSceneNode();
	smgr->setShadowColor(video::SColor(150, 0, 0, 0));
	g_renderData.chassisNode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

	g_renderData.groundNode[0] = smgr->addCubeSceneNode(1.0f, 0, -1, core::vector3df(0, -2, 0), core::vector3df(0, 0, 0), core::vector3df(200, 0.02, 200));
	g_renderData.groundNode[0]->setPosition(core::vector3df(0, -2, 0));

	//creazione del landscape da file

	g_renderData.groundNode[1] = smgr->addCubeSceneNode(1.0f, 0, -1, core::vector3df(10, -1, 0), core::vector3df(0, 0, -145.96915f), core::vector3df(10, 1, 10));
	for(int i = 0; i < 2; i++) {
		g_renderData.groundNode[i]->setMaterialTexture(0, driver->getTexture("./media/wall.bmp"));
		g_renderData.groundNode[i]->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
	}

	for(int i = 0; i < 10; i++) {
		g_renderData.wallNodes[i] = smgr->addCubeSceneNode(1.0f, 0, -1, core::vector3df(0, 0, 0), core::vector3df(0, 0, 0), core::vector3df(1.0f, 1.0f, 1.0f));
		g_renderData.wallNodes[i]->setMaterialTexture(0, driver->getTexture("./media/wall.bmp"));
		g_renderData.wallNodes[i]->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
	}

	Game* app = new Game();
	bool on = false;

	while(device->run()) {
		u32 kmph = app->m_vehicle->calcKMPH();
		core::stringw kmph_str(kmph);
		kmph_str += " km/h ";
		kmph_str += (u32)app->m_vehicle->calcRPM();
		kmph_str += " RPM ";
		if(on)
			kmph_str += (u32)app->m_vehicle->m_currentGear + 1;
		else
			kmph_str += (u32)app->m_vehicle->m_currentGear;

		if(app->m_vehicle->getChassis()->getPosition()(1) > (g_renderData.groundNode[0]->getPosition().Y + 2)) {
			kmph_str += " In Aria";
		} else {
			kmph_str += " A Terra";
		}

		device->setWindowCaption(kmph_str.c_str());

		if(receiver.IsKeyDown(KEY_ESCAPE)) {
			device->drop();
			return 0;
		}
		if(receiver.IsKeyDown(KEY_UP)) {
			on = true;
			app->accelerate = 0;
		} else if(receiver.IsKeyDown(KEY_DOWN)) {
			on = true;
			app->accelerate = 1;
		} else {
			app->accelerate = -1;
		}
		if(receiver.IsKeyDown(KEY_LEFT)) {
			app->steer = 1;
		} else if(receiver.IsKeyDown(KEY_RIGHT)) {
			app->steer = 0;
		} else {
			app->steer = -1;
		}
		if(receiver.IsKeyDown(KEY_KEY_R)) {
			app->resetVehicle();
		}

		driver->beginScene(true, true, video::SColor(255, 113, 113, 133));

		app->step();
		renderScene(app, driver, smgr);
		smgr->drawAll();

		u32 start = device->getTimer()->getRealTime();
		u32 left = device->getTimer()->getRealTime();
		while((left-start) < (FRAME_PERIOD * 1000)) {
			left = device->getTimer()->getRealTime();
		}

		driver->endScene();
	}

	device->drop();

	return 0;
}